package tools.illumina;

import java.io.BufferedWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;

import tools.fastq.FastqSeq;

public class ClonalityItem implements Comparable<ClonalityItem>{

	private String representiveSeq;
	private int representiveLength;
	private HashMap<Integer, HashSet<String>> store;
	
	public ClonalityItem(FastqSeq fqs){
		this.updateRepresentive(fqs.getSeq());
		store=new HashMap<Integer, HashSet<String>>();
		this.add(fqs);
	}
	
	//returns false if the add was successful
	public boolean add(FastqSeq fqs){
		final String tmpSeq=fqs.getSeq();
		final int tmpLength=tmpSeq.length();
		boolean added=false;
		if(tmpLength>this.getRepresentiveLength()){
			//longer
			if(tmpSeq.substring(0, getRepresentiveLength()).equals(getRepresentiveSeq())){
				//add and update
				store.put(tmpLength, new HashSet<String>());
				store.get(tmpLength).add(fqs.getQname());
				this.updateRepresentive(tmpSeq);
				added=true;
			}
		}else{
			//shorter or equal
			if(tmpSeq.equals(getRepresentiveSeq().substring(0, tmpLength))){
				//add
				if(!store.containsKey(tmpLength)){
					store.put(tmpLength, new HashSet<String>());
				}
				store.get(tmpLength).add(fqs.getQname());
				added=true;
			}
		}
		
		return added;
	}
	
	//adds a whole clonality item. only shorter
	public boolean addWholeClonalityItem(ClonalityItem ci){
		boolean added=false;
		final int ci_length=ci.getLength();
		if(this.getLength()>=ci_length){
			if(this.getRepresentiveSeq().substring(0, ci_length).equals(ci.getRepresentiveSeq())){
				//add all
				for (Integer size : ci.getSizes()) {
					if(this.store.containsKey(size)){
						this.store.get(size).addAll(ci.getNames(size));
					}else{
						this.store.put(size, ci.getNames(size));
					}
				}
				added=true;
			}
		}
		return added;
	}
	
	//
	public void addValidSubset(ClonalityItem ci){
		ArrayList<Integer> sizes=ci.getSizes();
		Collections.sort(sizes);
		int size;
		final String ciSeq=ci.getRepresentiveSeq();
		final String curSeq=this.getRepresentiveSeq();
		for(int i=sizes.size()-1;i>-1;i--){
			size=sizes.get(i);
			if(size<this.getLength()){
				if(ciSeq.substring(0, size).equals(curSeq.substring(0, size))){
					//add all names with a size smaller than the current
					for(;i>-1;i--){
						size=sizes.get(i);
						if(!this.store.containsKey(sizes)){
							this.store.put(size, new HashSet<String>());
						}
						this.store.get(size).addAll(ci.getNames(size));
					}
				}
			}
		}
	}
	
	public void print(BufferedWriter out,String cluster)throws Exception{
		for (String line : this.outList(cluster)) {
			out.write(line+"\n");
		}
	}
	
	public ArrayList<String> outList(String cluster)throws Exception{
		ArrayList<String> list= new ArrayList<String>();
		ArrayList<Integer> sizes=this.getSizes();
		Collections.sort(sizes);
		int lastSize=0,curCount=0;
		for (Integer curSize : sizes) {
			curCount+=this.countNs(this.getRepresentiveSeq().substring(lastSize, curSize));
			for (String name : this.getNames(curSize)) {
				list.add(name+"\t"+curSize+"\t"+curCount+"\t"+cluster+"\t"+this.getLength());
			}
			lastSize=curSize;
		}
		return list;
	}
	
	private int countNs(String s){
		int count=0;
		for(int i=0;i<s.length();i++){
			if(s.charAt(i)=='N'){
				count++;
			}
		}
		return count;
	}
	
	public ArrayList<Integer> getSizes(){
		return new ArrayList<Integer>(store.keySet());
	}
	
	public HashSet<String> getNames(int size){
		return store.get(size);
	}
	
	private void updateRepresentive(String seq){
		this.representiveSeq=seq;
		this.representiveLength=seq.length();
	}

	private String getRepresentiveSeq() {
		return representiveSeq;
	}

	private int getRepresentiveLength() {
		return representiveLength;
	}
	
	public int getLength(){
		return this.getRepresentiveLength();
	}

	public int compareTo(ClonalityItem arg0) {
		return this.getLength()-arg0.getLength();
	}
	
}
